package com.zyw.codegen.java;

import com.zyw.codegen.java.elements.AbstractMVCMethodGenerator;
import com.zyw.mvc.support.QueryResult;
import com.zyw.mvc.support.WrappedResult;
import com.zyw.orm.support.page.Page;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.dom.java.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

/**
 * @author zhangyw
 * @date 2017/3/14 14:15
 */
public class ControllerGenerator extends MVCGenerator {

    public ControllerGenerator() {
        super();
    }

    @Override
    public List<CompilationUnit> getCompilationUnits() {
        progressCallback.startTask(MessageFormat.format("Generating Controller for table {0}", //$NON-NLS-1$
                introspectedTable.getFullyQualifiedTable().toString()));

        TopLevelClass topLevelClass = getTopLevelClass();

        IntrospectedColumn primaryColumn = introspectedTable.getPrimaryKeyColumns().get(0);

        addGetBeanMethod(topLevelClass, primaryColumn);
        addSaveBeanMethod(topLevelClass);
        addUpdateBeanMethod(topLevelClass);
        // addDeleteBeanMethod(topLevelClass, primaryColumn);
        addFindByPageMethod(topLevelClass);
        List<CompilationUnit> answer = new ArrayList<CompilationUnit>();
        answer.add(topLevelClass);

        return answer;
    }

    private TopLevelClass getTopLevelClass() {
        TopLevelClass topLevelClass = new TopLevelClass(new FullyQualifiedJavaType(getControllerClassName()));
        topLevelClass.setVisibility(JavaVisibility.PUBLIC);
        context.getCommentGenerator().addJavaFileComment(topLevelClass);

        FullyQualifiedJavaType superClass = new FullyQualifiedJavaType(getRootClass());
        topLevelClass.setSuperClass(superClass);
        FullyQualifiedJavaType serviceClass = new FullyQualifiedJavaType(getServiceClassName());

        topLevelClass.addImportedType(superClass);
        topLevelClass.addImportedType(serviceClass);
        topLevelClass.addImportedType(new FullyQualifiedJavaType(Controller.class.getName()));
        topLevelClass.addImportedType(new FullyQualifiedJavaType(RequestMapping.class.getName()));
        topLevelClass.addImportedType(new FullyQualifiedJavaType(Autowired.class.getName()));
        topLevelClass.addImportedType(new FullyQualifiedJavaType(ResponseBody.class.getName()));
        topLevelClass.addAnnotation("@Controller");
        topLevelClass.addAnnotation("@RequestMapping(\"" + getBaseRequestPath() + "\")");

        Field field = new Field(StringUtils.uncapitalize(serviceClass.getShortName()), serviceClass);
        field.setVisibility(JavaVisibility.PRIVATE);
        field.addAnnotation("@Autowired");
        topLevelClass.addField(field);

        return topLevelClass;
    }

    private void addGetBeanMethod(TopLevelClass topLevelClass, final IntrospectedColumn primaryColumn) {
        @SuppressWarnings("rawtypes")
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType returnType = new FullyQualifiedJavaType(WrappedResult.class.getName());

                FullyQualifiedJavaType recordType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

                FullyQualifiedJavaType paramType = primaryColumn.getFullyQualifiedJavaType();

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(recordType);
                importedTypes.add(paramType);

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("get");
                String paramName = primaryColumn.getJavaProperty();
                method.addParameter(new Parameter(paramType, paramName));
                method.setReturnType(returnType);

                FullyQualifiedJavaType serviceClass = new FullyQualifiedJavaType(getServiceClassName());
                StringBuffer sb = new StringBuffer();
                sb.append("return wrappedSuccessResult(").append(StringUtils.uncapitalize(serviceClass.getShortName()))
                        .append(".get(").append(paramName).append("));");
                method.addBodyLine(sb.toString()); // $NON-NLS-1$
                method.addAnnotation("@RequestMapping(\"get\")");
                method.addAnnotation("@ResponseBody");

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }

    private void addSaveBeanMethod(TopLevelClass topLevelClass) {
        @SuppressWarnings("rawtypes")
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType returnType = new FullyQualifiedJavaType(WrappedResult.class.getName());

                FullyQualifiedJavaType paramType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(returnType);
                importedTypes.add(paramType);

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("save");
                String paramName = StringUtils.uncapitalize(paramType.getShortName());
                method.addParameter(new Parameter(paramType, paramName));
                method.setReturnType(returnType);

                FullyQualifiedJavaType serviceClass = new FullyQualifiedJavaType(getServiceClassName());
                StringBuffer sb = new StringBuffer();
                sb.append("int i = ").append(StringUtils.uncapitalize(serviceClass.getShortName())).append(".insert(")
                        .append(paramName).append(");");
                method.addBodyLine(sb.toString());
                sb.setLength(0);

                sb.append("if (i > 0) {");
                method.addBodyLine(sb.toString());
                sb.setLength(0);

                sb.append(" return wrappedSuccessResult(null);");
                method.addBodyLine(sb.toString());
                method.addBodyLine("} else {");
                sb.setLength(0);

                sb.append("return wrappedFaliedResult(\"保存失败\");");
                method.addBodyLine(sb.toString());
                method.addBodyLine("}");

                method.addAnnotation("@RequestMapping(\"save\")");
                method.addAnnotation("@ResponseBody");

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }


    private void addUpdateBeanMethod(TopLevelClass topLevelClass) {
        @SuppressWarnings("rawtypes")
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType returnType = new FullyQualifiedJavaType(WrappedResult.class.getName());

                FullyQualifiedJavaType paramType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(returnType);
                importedTypes.add(paramType);

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("update");
                String paramName = StringUtils.uncapitalize(paramType.getShortName());
                method.addParameter(new Parameter(paramType, paramName));
                method.setReturnType(returnType);

                FullyQualifiedJavaType serviceClass = new FullyQualifiedJavaType(getServiceClassName());
                StringBuffer sb = new StringBuffer();
                sb.append("int i = ").append(StringUtils.uncapitalize(serviceClass.getShortName())).append(".update(")
                        .append(paramName).append(");");
                method.addBodyLine(sb.toString());

                sb.setLength(0);
                sb.append("if (i > 0) {");
                method.addBodyLine(sb.toString());
                sb.setLength(0);
                sb.append(" return wrappedSuccessResult(null);");
                method.addBodyLine(sb.toString());
                method.addBodyLine("} else {");
                sb.setLength(0);
                sb.append("return wrappedFaliedResult(\"更新失败\");");
                method.addBodyLine(sb.toString());
                method.addBodyLine("}");

                method.addAnnotation("@RequestMapping(\"update\")");
                method.addAnnotation("@ResponseBody");

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }

    @SuppressWarnings({"unused", "rawtypes"})
    private void addFindByPageMethod(TopLevelClass topLevelClass) {
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType returnType = new FullyQualifiedJavaType(WrappedResult.class.getName());

                FullyQualifiedJavaType pageType = new FullyQualifiedJavaType(Page.class.getName());
                FullyQualifiedJavaType paramType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

                // 设置page 泛型
                pageType.addTypeArgument(paramType);

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(returnType);
                importedTypes.add(pageType);
                importedTypes.add(paramType);

                importedTypes.add(new FullyQualifiedJavaType(List.class.getName()));
                importedTypes.add(new FullyQualifiedJavaType(QueryResult.class.getName()));

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("query");

                String pageParamName = StringUtils.uncapitalize(pageType.getShortNameWithoutTypeArguments());
                String poParamName = StringUtils.uncapitalize(paramType.getShortName());

                method.addParameter(new Parameter(pageType, pageParamName));
                method.addParameter(new Parameter(paramType, poParamName));
                method.setReturnType(returnType);

                // 设置注解
                method.addAnnotation("@RequestMapping(\"list\")");
                method.addAnnotation("@ResponseBody");

                FullyQualifiedJavaType serviceClass = new FullyQualifiedJavaType(getServiceClassName());

                // 构建method body
                StringBuffer sb = new StringBuffer();
                // page.setParamBean(po);
                sb.append(pageParamName).append(".setParamBean(").append(poParamName).append(");");
                method.addBodyLine(sb.toString());
                sb.setLength(0);

                //List<User> list = this.userService.findByPage(page);
                sb.append("List<").append(paramType.getShortName()).append("> list = ")
                        .append(StringUtils.uncapitalize(serviceClass.getShortName()))
                        .append(".findByPage(").append(pageParamName).append(");");
                method.addBodyLine(sb.toString());
                sb.setLength(0);

//                QueryResult.QueryItem queryItem = new QueryResult.QueryItem();
//                queryItem.setRows(vos);
//                queryItem.setTotal(page.getTotal());
//                return wrappedSuccessResult(queryItem, "查询成功");
                sb.append("QueryResult.QueryItem queryItem = new QueryResult.QueryItem();");
                method.addBodyLine(sb.toString());
                sb.setLength(0);

                sb.append("queryItem.setRows(list);");
                method.addBodyLine(sb.toString());
                sb.setLength(0);

                sb.append("queryItem.setTotal(").append(pageParamName).append(".getTotal());");
                method.addBodyLine(sb.toString());
                sb.setLength(0);

                sb.append("return wrappedSuccessResult(queryItem, \"查询成功\");");
                method.addBodyLine(sb.toString());
                sb.setLength(0);

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }

    @SuppressWarnings({"unused", "rawtypes"})
    private void addDeleteBeanMethod(TopLevelClass topLevelClass, final IntrospectedColumn primaryColumn) {
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType returnType = new FullyQualifiedJavaType(int.class.getName());

                FullyQualifiedJavaType paramType = primaryColumn.getFullyQualifiedJavaType();

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(returnType);
                importedTypes.add(paramType);

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("delete");
                String paramName = primaryColumn.getJavaProperty();
                method.addParameter(new Parameter(paramType, paramName));
                method.setReturnType(returnType);
                method.addBodyLine("return getDao().deleteByPrimaryKey(" + paramName + ");");
                method.addAnnotation("@Transactional");

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private void initializeAndExecuteGenerator(AbstractMVCMethodGenerator methodGenerator, JavaElement javaElement) {
        methodGenerator.setContext(context);
        methodGenerator.setIntrospectedTable(introspectedTable);
        methodGenerator.setProgressCallback(progressCallback);
        methodGenerator.setWarnings(warnings);
        methodGenerator.addJavaElements(javaElement);
    }
}
